/*
 * Copyright (c) Tecplot, Inc.
 *
 * All rights reserved.  Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   - Redistributions of source code must retain the above copyright notice, this list of
 *     conditions and the following disclaimer.
 *   - Redistributions in binary form must reproduce the above copyright notice, this list
 *     of conditions and the following disclaimer in the documentation and/or other
 *     materials provided with the distribution.
 *   - Neither the name of the Tecplot, Inc., nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without specific
 *     prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef ComsolConverter_h_ /* Only include once */
#define ComsolConverter_h_

#include <string>
#include <exception>
#include <vector>
#include <ios>
#include <map>
#include "TECADDON.h"

class TecFile;

class ComsolConverter
{
public:
    ComsolConverter(const std::string& fileName);
    ~ComsolConverter();

    /**
     * Convert the file given in the constructor to Tecplot format.
     * Will throw Error if the file could not be read successfully.
     */
    void convertToTecplot(const std::string& destinationFile);

public:
    // public exception class
    class Error : public std::exception
    {
    public:
        Error(const std::string& message);
        ~Error() throw();

        virtual const char* what() throw();
    private:
        std::string m_errorMessage;
    };

private: // Inner classes, enums, typedefs
    typedef std::vector<LgIndex_t> ConnectivityList_t;
    typedef std::vector<float>     DataArray_t;

    enum ComsolZoneType_e
    {
        ComsolZoneType_Points,
        ComsolZoneType_Lines,
        ComsolZoneType_Triangular,
        ComsolZoneType_Tetrahedral,
        END_ComsolZoneType_e,
        ComsolZoneType_Invalid = BadEnumValue
    };

    struct ZoneInfo_s
    {
        ComsolZoneType_e   zoneType;
        LgIndex_t          numNodes;
        LgIndex_t          numElements;
        std::ios::pos_type nodeDataPosition;
        std::ios::pos_type elementDataPosition;
        std::vector<std::string> varNames;
        std::vector<std::ios::pos_type> solutionDataPositions;
        Strand_t strandID;

        ZoneInfo_s()
            : zoneType(ComsolZoneType_Invalid)
            , numNodes(0)
            , numElements(0)
            , nodeDataPosition(0)
            , elementDataPosition(0)
            , varNames()
            , solutionDataPositions()
            , strandID(0)
        {}

        void reset()
        {
            zoneType            = ComsolZoneType_Invalid;
            numNodes            = 0;
            numElements         = 0;
            nodeDataPosition    = 0;
            elementDataPosition = 0;
            solutionDataPositions.clear();
            strandID            = 0;
        }
    };

    struct SolutionData_s
    {
        std::string name;
        DataArray_t values;
    };

    struct ZoneData_s
    {
        DataArray_t          xVals;
        DataArray_t          yVals;
        DataArray_t          zVals;
        ConnectivityList_t   connectivity;
        std::vector<SolutionData_s> solutionData;
    };

    enum ComsolSection_e
    {
        ComsolSection_Coordinates,
        ComsolSection_Elements,
        ComsolSection_Data,
        ComsolSection_TimeParamEigenvalue,
        END_ComsolSection_e,
        ComsolSection_Invalid = BadEnumValue
    };

private: // Member functions
    void openFileForRead(const std::string& fileName,
                         std::ifstream&     stream) const;

    void scanFile(std::ifstream&           inputFile,
                  std::vector<ZoneInfo_s>& zoneInfoList);

    void readCoordinateHeader(const std::string& headerLine,
                              std::istream&      inputFile,
                              ComsolZoneType_e&  zoneType) const;

    void readElementHeader(const std::string& headerLine,
                           ComsolZoneType_e&  zoneType) const;

    void readDataHeader(const std::string& headerLine,
                        std::string&       varName) const;

    void readSolutionTimes(const std::string&  line,
                           std::vector<float>& solutionTimes);

    bool lineIsHeader(const std::string& line) const;

    ComsolSection_e determineHeaderType(const std::string& headerLine) const;

    void readZoneData(const ZoneInfo_s&        zoneInfo,
                      std::ifstream&           inputFile,
                      std::vector<ZoneData_s>& zoneData) const;

    void readCoordinateData(const ZoneInfo_s&        zoneInfo,
                            std::ifstream&           inputFile,
                            std::vector<ZoneData_s>& zoneData) const;

    void readConnectivityData(const ZoneInfo_s&   zoneInfo,
                              std::ifstream&      inputFile,
                              ConnectivityList_t& connectivity) const;

    void readSolutionData(const ZoneInfo_s&        zoneInfo,
                          std::ifstream&           inputFile,
                          std::vector<ZoneData_s>& zoneData) const;

    void createVariableList(const std::vector<ZoneInfo_s>& zoneInfoList,
                            std::vector<std::string>&      variables) const;

    void outputZoneData(const ZoneInfo_s&               zoneInfo,
                        const std::vector<ZoneData_s>&  zoneData,
                        const std::vector<std::string>& variables,
                        TecFile&                        outputFile) const;

    static void initZoneMap();

    static void initSectionMap();

    std::string readVariableNameFromHeader(std::string const& headerLine) const;

    void throwMissingVariableNameError(std::string const& headerLine) const;

private: // Member variables
    std::string        m_fileName;
    std::vector<float> m_solutionTimes;

    static const char m_comsolSectionDelim = '%';

    typedef std::map<ComsolZoneType_e, ZoneType_e> ZoneMap_t;
    static  ZoneMap_t m_zoneTypeMap;

    typedef std::map<std::string, ComsolSection_e> SectionMap_t;
    static SectionMap_t m_stringToSectionMap;
};

#endif
